#ifndef __JAZZY_JSON_HPP__
#define __JAZZY_JSON_HPP__

#ifndef DLLAPI
#define DLLAPI
#endif//DLLAPI

#include <iosfwd>
#include <vector>

namespace Jazzy {
    
    class DLLAPI JsonReader {
    public:
        struct Type { enum Enum {
            String,
            Number,
            Object,
            Array,
            Boolean,
            Null
        };};
        
        //=== Initialization ===
        
        /// Create an empty Reader. One needs to call setDocument later.
        JsonReader();
        /// Creates and initialize a Reader at the same time. The content of the stream will be read on subsequent calls to "read()".
        JsonReader(std::istream & stream);
        
        /// Initializes the Reader. The content of the stream will be read on subsequent calls to "read()".
        void         setDocument( std::istream & stream);
        
        
        //=== Parsing
        
        /// Read the next tocken. Returns true if the tocken was read properly, false otherwise. Suggested usage: while(myReader.read())
        bool         read();
        
        /// Returns the value of the current tocken
        Type::Enum   getValueType() const;
        
        // --- Semantic method: gives the role of the current tocken plays in the document.
        bool         isObjectOpening() const;
        bool         isFieldName() const;
        bool         isFieldValue() const;
        bool         isObjectClosure() const;
        bool         isArrayOpening() const;
        bool         isArrayElement() const;
        bool         isArrayClosure() const;
        
        
        //--- Current Token access
        /// Returns the value of the current token as a string. The string value can be accessed with any type of data.
        char const * getValueAsString() const;
        /// Returns the value of the current token as a double. Reports an error if the value is not a number
        double       getValueAsDouble() const;
        /// Returns the value of the current token as a float. Reports an error if the value is not a number.
        float        getValueAsFloat() const;
        /// Returns the value of the current token as an int. Reports an error if the value is not a number.
        int          getValueAsInt() const;
        /// Returns the value of the current token as a bool. Reports an error if the value is not a bool.
        bool         getValueAsBool() const;        
        
        // - Convenience:
        
        /// Try getting the value as a double. Returns true and assign the token value in the argument if successful, returns false otherwise.
        double       tryGetValue(double & value) const;
        /// Try getting the value as a float. Returns true and assign the token value in the argument if successful, returns false otherwise.
        float        tryGetValue(float & value) const;
        /// Try getting the value as an int. Returns true and assign the token value in the argument if successful, returns false otherwise.
        int          tryGetValue(int & value) const;
        /// Try getting the value as a bool. Returns true and assign the token value in the argument if successful, returns false otherwise.
        bool         tryGetValue(bool & value) const;
        
        /// Returns true if the current token is null
        bool         isNull() const;
        
        
        //=== Error Handling & other settings ===
        
        /// Ugly C-like interface for setting an error handler. Using delegates or signal/slot would be much better.
        /// Using such an interface simply allows the JsonReader to be self-contained.
        /// The default behavior is to crash violently upon errors.
        /// To set the error handler, use getSettings().setErrorHandler(customHandler)
        struct DLLAPI ErrorHandler {
            typedef void (*HandlerFunction)(void *, char const *);
            ErrorHandler();
            ErrorHandler(HandlerFunction function, void * value);
            bool operator()(char const * pMessage) const;
            
            HandlerFunction m_function;
            void *          m_pValue;
        };
        
        /// Settings
        class DLLAPI Settings {
        public:
            Settings();
            
            /// Gets this instance's error handler (useful for backups)
            ErrorHandler const & getErrorHandler() const;
            
            /// Sets this instance's error handler. Returns a reference to itself for the named parameter idiom.
            Settings &           setErrorHandler(ErrorHandler errorHandler);
            
            /// Returns true if the comma is mandatory between fields, false otherwise.
            bool                 isCommaMandatoryBetweenFields() const;
            
            /// Tell the reader to consider commas mandatory between fields. Returns a reference to itself for the named parameter idiom.
            Settings &           setCommaMandatoryBetweenFields(bool value);
            
        private:
            ErrorHandler m_errorHandler;
            bool         m_isCommaMandatoryBetweenFields;
        };

        
        /// Get the default settings. If modified, the new settings will apply to all new JsonReaders. Also useful for backups and scoped changes.
        static Settings &       getDefaultSettings();
        /// After calling this method, all newly created JsonReaders will be initialized with these settings
        static void             setDefaultSettings(Settings const & settings);
        /// Set settings for this particular JsonReader instance.
        void                    setSettings(Settings const & settings);
        /// Returns this instances' settings.
        Settings const &        getSettings() const;
        /// Returns this instances' settings.
        Settings &              getSettings();
        
    private:
        class Private; ///< All helper methods are implemented in here, in the CPP. No need to expose them in the header, even privately.
        
        std::istream * m_pStream;
        
        struct DLLAPI Node {
            Node();
            Type::Enum          m_type;
            std::vector< int >  m_parsingContext;
            std::vector< char > m_text;
            
            // Based on the type, only one of these members is relevant.
            union {
                double                m_number;
                bool                  m_boolean;
            };
        }
        m_node;
        
        Settings m_settings;
    };
}
#endif//__JAZZY_JSON_HPP__
